package com.yjc.drygoods.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.yjc.common.constant.InventoryAlertsProcessStatusEnum;
import com.yjc.common.constant.InventoryAlertsReasonEnum;
import com.yjc.common.constant.OldTimersReasonEnum;
import com.yjc.common.constant.OldTimersStatusEnum;
import com.yjc.common.to.OrderTo;
import com.yjc.common.to.SpuCommentCountTo;
import com.yjc.common.to.SpuHasStock;
import com.yjc.common.to.StockLockTo;
import com.yjc.common.utils.R;
import com.yjc.common.exception.NotWareStockException;
import com.yjc.drygoods.ware.dao.WareSpuDao;
import com.yjc.drygoods.ware.entity.*;
import com.yjc.drygoods.ware.exception.NotExitStockException;
import com.yjc.drygoods.ware.service.*;
import com.yjc.drygoods.ware.vo.OrderItemVo;
import com.yjc.drygoods.ware.vo.OrderVo;
import com.yjc.drygoods.ware.vo.WareLockVo;
import com.yjc.drygoods.ware.feign.OrderFeignService;
import com.yjc.drygoods.ware.feign.ProductFeignService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yjc.common.utils.PageUtils;
import com.yjc.common.utils.Query;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Slf4j
@Service("wareSkuService")
public class WareSpuServiceImpl extends ServiceImpl<WareSpuDao, WareSpuEntity> implements WareSpuService {

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    OrderFeignService orderFeignService;

    @Autowired
    InventoryAlertsService inventoryAlertsService;

    @Autowired
    BatchesService batchesService;

    @Autowired
    PurchaseDetailService purchaseDetailService;

    @Autowired
    OldTimersService oldTimersService;

    @Autowired
    WareInfoService wareInfoService;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSpuEntity> queryWrapper = new QueryWrapper<>();
        /**
         * wareId: 123,//仓库id
         *    skuId: 123//商品id
         */
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        String spuName = (String) params.get("spuName");
        if (!StringUtils.isEmpty(spuName)) {
            queryWrapper.like("spu_name", spuName);
        }
        IPage<WareSpuEntity> page = this.page(
                new Query<WareSpuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 扣除真实库存，还原锁定库存
     *
     * @param spuId
     * @param wareId
     * @param skuNum
     */
    @Override
    public void deductRealStock(Long spuId, Long wareId, Double skuNum) {
        QueryWrapper<WareSpuEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("spu_id", spuId).eq("ware_id", wareId);
        Integer count = this.baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            this.baseMapper.deleteRealStock(spuId, wareId, skuNum);
        } else {
            throw new RuntimeException("没有该干货库存");
        }
    }

    /**
     * @param spuId  商品id
     * @param wareId 仓库id
     * @param skuNum 要新增的商品数量
     */
    @Override
    public void updateWareStock(Long spuId, Long wareId, Double skuNum) {
        QueryWrapper<WareSpuEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("spu_id", spuId).eq("ware_id", wareId);
        Integer count = this.baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            this.baseMapper.updateStock(spuId, wareId, skuNum);
        } else {
            WareInfoEntity wareData = wareInfoService.getById(wareId);
            WareSpuEntity wareSpuEntity = new WareSpuEntity();
            wareSpuEntity.setSpuId(spuId);
            wareSpuEntity.setWareId(wareId);
            wareSpuEntity.setWareName(wareData.getName());
            wareSpuEntity.setStock(skuNum);
            //TODO 还有其他方法可以使异常出现后不会回滚，例如服务熔断,sentinel
            try {
                //修改成从spuinfocontroller获取信息
                R info = productFeignService.spuInfo(spuId);
                if (info.getCode() == 0) {
                    Map<String, Object> spuInfo = (Map<String, Object>) info.get("spuInfo");
                    wareSpuEntity.setSpuName((String) spuInfo.get("spuName"));
                }
            } catch (Exception e) {

            }
            this.baseMapper.insert(wareSpuEntity);
        }
    }

    @Override
    public Double getSingleSpuWareStock(Long spuId) {
        Double aDouble = this.baseMapper.selectHasStock(spuId);
        log.info("aDouble:{}", aDouble);
        return aDouble;
    }

    @Override
    public List<SpuHasStock> getHasStock(List<Long> spuIds) {
        List<SpuHasStock> spuHasStocks = spuIds.stream().map(spuId -> {
            Double count = this.baseMapper.selectHasStock(spuId);
            SpuHasStock spuHasStock = new SpuHasStock();
            spuHasStock.setSpuId(spuId);
            if (count != null) {
                //库存斤重量大于等于1斤，有货
                spuHasStock.setHashStock(count >= 1);
            } else {
                spuHasStock.setHashStock(false);
            }
            return spuHasStock;
        }).collect(Collectors.toList());
        return spuHasStocks;
    }


    @Transactional
    @Override
    public Boolean lockStock(WareLockVo wareLockVo) {

        log.info("wareLockVo:{}", wareLockVo);
        //库存任务单
        WareOrderTaskEntity stockTask = new WareOrderTaskEntity();
        stockTask.setOrderSn(wareLockVo.getOrderSn());
        wareOrderTaskService.save(stockTask);

        List<OrderItemVo> locks = wareLockVo.getLocks();
        log.info("lock:{}", locks);
        List<SpuWareHashStock> spuWareHashStocks = locks.stream().map(lock -> {
            SpuWareHashStock spuWareHashStock = new SpuWareHashStock();
            spuWareHashStock.setSpuId(lock.getSpuId());
            //一个商品可能存在很多个仓库
            List<Long> wareIds = this.baseMapper.selectWareSpuStockId(lock.getSpuId());
            spuWareHashStock.setWareIds(wareIds);
            log.info("lock.getWeight():{}", lock.getWeight());
            spuWareHashStock.setCount(lock.getCount() * lock.getWeight());
            return spuWareHashStock;
        }).collect(Collectors.toList());
        for (SpuWareHashStock spuWareHashStock : spuWareHashStocks) {
            boolean isLocked = false;
            if (spuWareHashStock.getWareIds() == null || spuWareHashStock.getWareIds().size() == 0) {
                throw new NotWareStockException(spuWareHashStock.getSpuId());
            }
            List<Long> wareIds = spuWareHashStock.getWareIds();
            for (Long wareId : wareIds) {
                Long res = this.baseMapper.updateSpuWareHashStock(spuWareHashStock.getSpuId(), spuWareHashStock.getCount(), wareId);
                if (res == 1) {
                    //找到了库存足够的仓库
                    isLocked = true;

                    //发送锁定库存消息
                    StockLockTo stockLockTo = new StockLockTo();
                    stockLockTo.setStockTaskId(stockTask.getId());
                    WareOrderTaskDetailEntity stockTaskDetail = new WareOrderTaskDetailEntity();

                    stockTaskDetail.setSpuId(spuWareHashStock.getSpuId());
                    stockTaskDetail.setSkuNum(spuWareHashStock.getCount());
                    stockTaskDetail.setWareId(wareId);
                    stockTaskDetail.setTaskId(stockTask.getId());
                    stockTaskDetail.setLockStatus(1);
                    wareOrderTaskDetailService.save(stockTaskDetail);
                    stockLockTo.setStockTaskDetailId(stockTaskDetail.getId());
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockTo);
                    break;
                }
            }
            if (!isLocked) {
                throw new NotWareStockException(spuWareHashStock.getSpuId());
            }
        }
        return true;
    }

    @Override
    public void unLocked(StockLockTo stockLockTo) {
        Long stockTaskId = stockLockTo.getStockTaskId();//库存任务单id
        Long stockTaskDetailId = stockLockTo.getStockTaskDetailId();//库存任务详情id
        WareOrderTaskDetailEntity stockTaskDetail = wareOrderTaskDetailService.getById(stockTaskDetailId);
        if (stockTaskDetail != null) {
            //库存服务完成无异常
            WareOrderTaskEntity stockTask = wareOrderTaskService.getById(stockTaskId);
            String orderSn = stockTask.getOrderSn();
            R res = orderFeignService.getOrderInfoByOrderSn(orderSn);
            if (res.getCode() == 0) {
                OrderVo data = res.getData(new TypeReference<OrderVo>() {
                });
                if (data == null || data.getStatus() == 4) {
                    if (stockTaskDetail.getLockStatus() == 1) {
                        unLocked(stockTaskDetail.getSpuId(), stockTaskDetail.getSkuNum(), stockTaskDetail.getWareId(), stockTaskDetailId);
                    }
                }
            } else {
                throw new RuntimeException("远程服务调用失败");
            }
        } else {
            //库存服务出现异常回滚
        }
    }

    @Override
    public void unLocked(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        WareOrderTaskEntity stockTask = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        Long stockTaskId = stockTask.getId();
        List<WareOrderTaskDetailEntity> stockTaskDetails = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", stockTaskId)
                .and(i -> i.eq("lock_status", 1)));
        if (stockTaskDetails != null && stockTaskDetails.size() > 0) {
            for (WareOrderTaskDetailEntity stockTaskDetail : stockTaskDetails) {
                unLocked(stockTaskDetail.getSpuId(), stockTaskDetail.getSkuNum(), stockTaskDetail.getWareId(), stockTaskDetail.getId());
            }
        }
    }

    @Override
    public Boolean getSingleSpuWare(Long spuId) {

        List<WareSpuEntity> wareSpus = this.list(new QueryWrapper<WareSpuEntity>().eq("spu_id", spuId));
        if (wareSpus == null || wareSpus.size() <= 0) {
            return false;
        }
        //统一该spuId干货在所有仓库的库存存储信息
        WareSpuEntity wareSpuTotalStock = new WareSpuEntity();
        for (WareSpuEntity wareSpu : wareSpus) {
            wareSpuTotalStock.setStock((wareSpuTotalStock.getStock() == null ? 0 : wareSpuTotalStock.getStock()) + wareSpu.getStock());
            wareSpuTotalStock.setStockLocked((wareSpuTotalStock.getStockLocked() == null ? 0 : wareSpuTotalStock.getStockLocked()) + wareSpu.getStockLocked());
        }
        Double stockNum = wareSpuTotalStock.getStock() - wareSpuTotalStock.getStockLocked();
        if (stockNum < 1) {
            return false;
        }
        return true;

    }


    /**
     * 解锁锁定库存
     */
    public void unLocked(Long spuId, Double skuNum, Long wareId, Long taskDetailId) {
        this.baseMapper.unLocked(spuId, skuNum, wareId, taskDetailId);
        WareOrderTaskDetailEntity taskDetail = new WareOrderTaskDetailEntity();
        taskDetail.setId(taskDetailId);
        taskDetail.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetail);
    }

    /**
     * 定时任务：
     * 1.将上一天未处理或是库存未清空状态的预警进行过期处理
     * 2.每天晚上23点扫描每种商品的库存数量，低于8斤的，统一加入库存预警表中，并给出预警原因
     */
    @XxlJob("checkWareSpuNum")
    public void checkWareSpuNum() {
        //1.将上一天未处理或是库存未清空状态的预警进行过期处理
        List<InventoryAlertsEntity> processStatusList = inventoryAlertsService.list(new QueryWrapper<InventoryAlertsEntity>()
                .ne("process_status", InventoryAlertsProcessStatusEnum.PROCESSED.getCode())
                .ne("process_status", InventoryAlertsProcessStatusEnum.THE_ALERT_EXPIRED.getCode()));
        List<InventoryAlertsEntity> updateProcessStatusData = processStatusList.stream().map(item -> {
            item.setProcessStatus(InventoryAlertsProcessStatusEnum.THE_ALERT_EXPIRED.getCode());
            return item;
        }).collect(Collectors.toList());
        inventoryAlertsService.updateBatchById(updateProcessStatusData);

        ////////////////////////////////////////////

        List<WareSpuEntity> list = this.list();
        log.info("list：{}", list);
        //2.每天晚上23点扫描每种商品的库存数量，低于8斤的统一加入库存预警表中，并给出预警原因
        Map<Long, Double> stockMap = new HashMap<>();
        for (WareSpuEntity wareSpu : list) {
            if (stockMap.get(wareSpu.getSpuId()) == null) {
                stockMap.put(wareSpu.getSpuId(), wareSpu.getStock() - wareSpu.getStockLocked());
            } else {
                stockMap.put(wareSpu.getSpuId(), stockMap.get(wareSpu.getSpuId()) + (wareSpu.getStock() - wareSpu.getStockLocked()));
            }
        }
        log.info("stockMap：{}", stockMap);
        //当spuId重复时，取第一个
        Map<Long, WareSpuEntity> resStock = list.stream()
                .collect(Collectors.toMap(WareSpuEntity::getSpuId, item -> item, (n1, n2) -> n1));
        log.info("resStock：{}", resStock);
        List<WareSpuEntity> values = new ArrayList<>(resStock.values());
        log.info("values：{}", values);
        //找出低于3斤的干货
        List<WareSpuEntity> lackLockSpus = values.stream()
                .filter(item -> stockMap.get(item.getSpuId()) < 8)
                .map(item -> {
                    item.setStock(stockMap.get(item.getSpuId()));
                    return item;
                }).collect(Collectors.toList());
        log.info("lackLockSpus：{}", lackLockSpus);
        List<Long> spuIds = lackLockSpus.stream().map(WareSpuEntity::getSpuId).collect(Collectors.toList());
        if (lackLockSpus == null || lackLockSpus.size() <= 0) {
            return;
        }
        List<BatchesEntity> batches = batchesService.list(new QueryWrapper<BatchesEntity>().in("spu_id", spuIds));
        Map<Long, Integer> batchMap = batches.stream().collect(Collectors.toMap(BatchesEntity::getSpuId, BatchesEntity::getBatch));
        //此时的lackLockSpus里的stock相当于该干货的所有仓库的真实库存
        List<InventoryAlertsEntity> array = new ArrayList<>();
        for (WareSpuEntity lackLockSpu : lackLockSpus) {
            InventoryAlertsEntity inventoryAlert = new InventoryAlertsEntity();
            inventoryAlert.setSpuId(lackLockSpu.getSpuId());
            inventoryAlert.setSpuName(lackLockSpu.getSpuName());
            inventoryAlert.setSpuNum(lackLockSpu.getStock());
            inventoryAlert.setWarningTime(new Date());
            inventoryAlert.setBatch(batchMap.get(lackLockSpu.getSpuId()) == null ? 0 : batchMap.get(lackLockSpu.getSpuId()));
            if (lackLockSpu.getStock() >= 1) {
                //快无货
                inventoryAlert.setWarningReason(InventoryAlertsReasonEnum.INVENTORY_IS_OUT.getCode());
            } else {
                //已无货
                inventoryAlert.setWarningReason(InventoryAlertsReasonEnum.OUT_OF_STOCK.getCode());
            }
            //刚加入库存预警，统一未处理
            inventoryAlert.setProcessStatus(InventoryAlertsProcessStatusEnum.CREATE.getCode());
            inventoryAlert.setProcessTime(new Date());
            array.add(inventoryAlert);
        }
        //批量添加干货预警信息
        inventoryAlertsService.saveBatch(array);
    }

    /**
     * 定时任务：
     * 1.每天定时扫描干货库存表，将5天后，未售出，或者售出重量小于5斤的加入陈年老货表，并给出原因
     */
    @XxlJob("checkSpuWareStockAddOldTimers")
    public void checkSpuWareStockAddOldTimers() {
        List<WareSpuEntity> list = this.list();
        log.info("list：{}", list);
        Map<Long, Double> stockMap = new HashMap<>();
        for (WareSpuEntity wareSpu : list) {
            if (stockMap.get(wareSpu.getSpuId()) == null) {
                stockMap.put(wareSpu.getSpuId(), wareSpu.getStock() - wareSpu.getStockLocked());
            } else {
                stockMap.put(wareSpu.getSpuId(), stockMap.get(wareSpu.getSpuId()) + (wareSpu.getStock() - wareSpu.getStockLocked()));
            }
        }
        log.info("stockMap：{}", stockMap);
        //当spuId重复时，取第一个
        Map<Long, WareSpuEntity> resStock = list.stream()
                .collect(Collectors.toMap(WareSpuEntity::getSpuId, item -> item, (n1, n2) -> n1));
        log.info("resStock：{}", resStock);
        List<WareSpuEntity> values = new ArrayList<>(resStock.values());
        log.info("values：{}", values);
        List<WareSpuEntity> allWareSpus = values.stream()
                .map(item -> {
                    item.setStock(stockMap.get(item.getSpuId()));
                    return item;
                }).collect(Collectors.toList());
        log.info("allWareSpus：{}", allWareSpus);
        List<Long> spuIds = allWareSpus.stream().map(WareSpuEntity::getSpuId).collect(Collectors.toList());
        if (allWareSpus == null || allWareSpus.size() <= 0) {
            return;
        }
        List<BatchesEntity> batches = batchesService.list(new QueryWrapper<BatchesEntity>().in("spu_id", spuIds));
        //所有干货的采购数量(map形式表示)
        Map<Long, Double> spuNumMap = batches.stream().collect(Collectors.toMap(BatchesEntity::getSpuId, BatchesEntity::getSpuNum));
        //整理出spuId对应的最新批次的采购需求
        Map<Long, BatchesEntity> spuBatchMap = batches.stream().collect(Collectors.toMap(BatchesEntity::getSpuId, item -> item));

        //保存所有的最新批次干货的采购需求信息
//        List<PurchaseDetailEntity> purchaseDetailList = new LinkedList<>();
        //所有干货的采购数量(map形式表示)
//        Map<Long, Double> purchaseDetailMap = batches.stream().map(item -> {
//            PurchaseDetailEntity purchaseDetail = purchaseDetailService.getOne(new QueryWrapper<PurchaseDetailEntity>()
//                    .eq("spu_id", item.getSpuId())
//                    .eq("batch", item.getBatch()));
//            purchaseDetailList.add(purchaseDetail);
//            return purchaseDetail;
//        }).collect(Collectors.toMap(PurchaseDetailEntity::getSpuId, PurchaseDetailEntity::getSpuNum));
        //整理出spuId对应的最新批次的采购需求
//        Map<Long, PurchaseDetailEntity> spuIdPurchaseDetailMap = purchaseDetailList
//                .stream()
//                .collect(Collectors.toMap(PurchaseDetailEntity::getSpuId, item -> item));

        Map<Long, Date> updateTimeMap = batches
                .stream()
                .collect(Collectors.toMap(BatchesEntity::getSpuId, BatchesEntity::getUpdateTime));
        //查询出陈年老货(超过5天，未售出，或者售出重量小于5斤)
        Calendar calenderNow = Calendar.getInstance();
        //当前天
        long day = new Date().getTime();
        List<OldTimersEntity> oldTimers = allWareSpus.stream()
                .filter(item -> (spuNumMap.get(item.getSpuId()) - item.getStock()) < 5)
                .map(item -> {
                    Long updateDay = updateTimeMap.get(item.getSpuId()).getTime();
                    long num = day - updateDay;
                    log.info("day:{},updateDay:{},相隔天数:{}", day, updateDay, (num / 24 / 60 / 60 / 1000));
                    if ((num / 24 / 60 / 60 / 1000) > 5) {
                        OldTimersEntity oldTimersEntity = new OldTimersEntity();
                        oldTimersEntity.setCreateTime(new Date());
                        oldTimersEntity.setSpuId(item.getSpuId());
                        oldTimersEntity.setSpuName(item.getSpuName());
                        oldTimersEntity.setSpuNum(item.getStock());
                        //设置陈年老货状态信息
                        //1.获取该SPU干货所有评论信息
                        oldTimersEntity.setStatus(OldTimersStatusEnum.NOT_PROCESSED.getCode());
                        BatchesEntity batchesEntity = spuBatchMap.get(item.getSpuId());
                        if (batchesEntity == null) {
                            throw new RuntimeException("未找到该spu干货的采购需求");
                        }
                        //设置批次信息
                        oldTimersEntity.setBatch(batchesEntity.getBatch());
                        //设置供货商id
                        oldTimersEntity.setSupplierId(batchesEntity.getSupplierId());
                        //设置供货商名称
                        oldTimersEntity.setSupplierName(batchesEntity.getSupplierName());
                        //设置积压原因-->查询该干货的评论信息。分析：如果平均星级大于等于3星，则是干货此时不适合大量销售，否则是质量问题
                        R r = productFeignService.getAllSpuComments();
                        Integer avgStar = (Integer) r.get("data");
                        oldTimersEntity.setCause(avgStar >= 3 ? OldTimersReasonEnum.THE_TIMING_OF_THE_SALE_NOT_RIGHT.getCode() :
                                OldTimersReasonEnum.LOW_QUALITY.getCode());
                        //已存在该spuId的陈年老货，则更新信息
                        oldTimersService.saveOrUpdate(oldTimersEntity, new QueryWrapper<OldTimersEntity>()
                                .eq("spu_id", oldTimersEntity.getSpuId())
                                .ne("status", OldTimersStatusEnum.PROCESSED.getCode()));
                        return oldTimersEntity;
                    }
                    return null;
                }).filter(item -> item != null).collect(Collectors.toList());
        log.info("oldTimers:{}", oldTimers);

    }

    @Override
    public void clearNeedExchangeSpuStock(Long spuId) {
        this.baseMapper.clearNeedExchangeSpuStock(spuId);
    }


    /**
     * 每个商品的所在仓库信息
     */
    @Data
    class SpuWareHashStock {
        private Long spuId;
        private Double count;
        private List<Long> wareIds;
    }

}